#include "sample_svp_nnie_software.h"  // SAMPLE_SVP_NNIE_QUANT_BASE
   
#include <time.h>

# include "efficientdet.h"

/* *****************************************************
* 统计耗时
******************************************************* */

static struct timespec start_time;
static struct timespec end_time;
static struct timespec total_start_time;
static struct timespec total_end_time;

// 打印时间 
double PCPrintTime(HI_CHAR* str, HI_BOOL print_total_time, HI_BOOL add_total_time, HI_BOOL print_single){
    static double use_time_total = 0; 
    double use_time = 0; 
    clock_gettime(CLOCK_REALTIME, &end_time); // 计时
    use_time = (double)(end_time.tv_sec - start_time.tv_sec)*1000 + (end_time.tv_nsec - start_time.tv_nsec) / 1000000.0;
    if(add_total_time) 
        use_time_total += use_time;
    if(EFFICIENTDET_PRINT_FLAG && print_single){
        printf("%s: %.5fms\n", str, use_time);
        if(print_total_time) 
            printf("total time: %.5fms  FPS:%.5f\n", use_time_total, 1000/use_time_total);
    }
    if(print_total_time){
        use_time_total = 0;
    }
    start_time.tv_sec = end_time.tv_sec;
    start_time.tv_nsec = end_time.tv_nsec;

    return use_time;
}

// 开始计时
void start_reckon_by_time(){
    clock_gettime(CLOCK_REALTIME, &start_time); // 开始计时
    clock_gettime(CLOCK_REALTIME, &total_start_time); // 开始计时
}

// 打印平均时间  多用于多线程 
double Efficient_mean_time(int img_num){
    clock_gettime(CLOCK_REALTIME, &total_end_time); // 计时
    double use_time = (double)(total_end_time.tv_sec - total_start_time.tv_sec)*1000 + 
                      (total_end_time.tv_nsec - total_start_time.tv_nsec) / 1000000.0;
    use_time /= img_num;
    if(EFFICIENTDET_PRINT_FLAG)
        printf("mean time: %.5fms  FPS:%.5f\n", use_time, 1000/use_time);
    return use_time;
}


/* *****************************************************
* IVE: yuv-> bgr
******************************************************* */
HI_S32 Efficient_YUV2BGR(VIDEO_FRAME_INFO_S *pstInput_yuv, SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam, 
                         SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S* pstInputDataIdx){
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U8 i_circul; 
    HI_BOOL bFinish;
    HI_BOOL bBlock = HI_TRUE;
    IVE_HANDLE hIveHandle;
    HI_BOOL bInstant = HI_TRUE;

    // IVE_CSC_MODE_PIC_BT709_YUV2RGB  IVE_CSC_MODE_PIC_BT601_YUV2RGB  IVE_CSC_MODE_VIDEO_BT709_YUV2RGB   IVE_CSC_MODE_VIDEO_BT601_YUV2RGB
    IVE_CSC_CTRL_S stCscCtrl;
    stCscCtrl.enMode = IVE_CSC_MODE_PIC_BT601_YUV2RGB; 

    // 填充yuv图片
    IVE_SRC_IMAGE_S stSrcData;
    stSrcData.enType = IVE_IMAGE_TYPE_YUV420SP;
    for(i_circul = 0; i_circul < 3; i_circul++)
    {
        stSrcData.au64PhyAddr[i_circul] = pstInput_yuv->stVFrame.u64PhyAddr[i_circul];
        stSrcData.au64VirAddr[i_circul] = pstInput_yuv->stVFrame.u64VirAddr[i_circul];
        stSrcData.au32Stride[i_circul] = pstInput_yuv->stVFrame.u32Stride[i_circul];
        // printf("%dth src stride: %d\n", i_circul, stSrcData.au32Stride[i_circul]);
    }
    stSrcData.u32Width = pstInput_yuv->stVFrame.u32Width;
    stSrcData.u32Height = pstInput_yuv->stVFrame.u32Height;
    // printf("yuv  w:%d h:%d\n", stSrcData.u32Width, stSrcData.u32Height);

    // 创建输出图像的参数、类型
    IVE_DST_IMAGE_S stDstData; // csc只支持这个类型的参数输入，因此将要存图像的地址，赋给这个参数
    stDstData.enType = IVE_IMAGE_TYPE_U8C3_PLANAR;
    for(i_circul = 0; i_circul < 3; i_circul++){
        stDstData.au64PhyAddr[i_circul] = pstNnieParam->astSegData[pstInputDataIdx->u32SegIdx].astSrc[pstInputDataIdx->u32NodeIdx].u64PhyAddr 
            + i_circul*SAMPLE_SVP_NNIE_ALIGN16(EFFICIENTDET_INPUT_WIDTH)*EFFICIENTDET_INPUT_HEIGHT; // 跨度304 高300 一个通道，91200
        stDstData.au64VirAddr[i_circul] = pstNnieParam->astSegData[pstInputDataIdx->u32SegIdx].astSrc[pstInputDataIdx->u32NodeIdx].u64VirAddr 
            + i_circul*SAMPLE_SVP_NNIE_ALIGN16(EFFICIENTDET_INPUT_WIDTH)*EFFICIENTDET_INPUT_HEIGHT;
        stDstData.au32Stride[i_circul] = pstNnieParam->astSegData[pstInputDataIdx->u32SegIdx].astSrc[pstInputDataIdx->u32NodeIdx].u32Stride;
    }
    stDstData.u32Width = pstNnieParam->astSegData[pstInputDataIdx->u32SegIdx].astSrc[pstInputDataIdx->u32NodeIdx].unShape.stWhc.u32Width;
    stDstData.u32Height = pstNnieParam->astSegData[pstInputDataIdx->u32SegIdx].astSrc[pstInputDataIdx->u32NodeIdx].unShape.stWhc.u32Height;
    // printf("bgr  w:%d h:%d\n", stDstData.u32Width, stDstData.u32Height); // pelee是这里出现了问题 转换图像设置为300，应该为304

    // yuv 转换 成bgr 开始
    s32Ret = HI_MPI_IVE_CSC(&hIveHandle, &stSrcData, &stDstData, &stCscCtrl, bInstant);
    if(HI_SUCCESS != s32Ret){   
        printf("Error!! IVE_CSC , code:%d\n", s32Ret);
        return s32Ret;
    }

    // 等待转换完成
    if (HI_TRUE == bInstant){
        s32Ret = HI_MPI_IVE_Query(hIveHandle,&bFinish,bBlock);
        while(HI_ERR_IVE_QUERY_TIMEOUT == s32Ret){
            usleep(100);
            s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
        }

        if(HI_SUCCESS != s32Ret){
            printf("Error!! IVE_Query , code:%d\n", s32Ret);
            return s32Ret;
        }
    }
    return s32Ret;
}


/* *****************************************************
* vdec: JPG -> YUV
******************************************************* */
// 读取jpg图片
HI_S32 Efficient_Read_JPG(FILE *fp_list, HI_U8 *JPGfile_pu8Buf, HI_CHAR cStreamFile[], VDEC_STREAM_S *pstStream, VDEC_THREAD_PARAM_S stVdecSend[]){
    HI_S32 s32Ret = HI_SUCCESS;
    // 获取图片路径
    if(fgets(cStreamFile, 1023, fp_list) == NULL) return HI_FAILURE;
    cStreamFile[strlen(cStreamFile) - 1] = '\0';

    // 打开JPG
    FILE *fpStrm = NULL; // JPG文件指针
    if(cStreamFile != 0){
        fpStrm = fopen(cStreamFile, "rb");
        if(fpStrm == NULL){
            printf("can't open file %s in send stream thread!\n", cStreamFile);
            return HI_FAILURE;
        }
    }
    // 读取jpg数据 
    HI_S32 s32ReadLen = fread(JPGfile_pu8Buf, 1, stVdecSend[0].s32MinBufSize, fpStrm); // 从数据流读取 1size bufsize个数据到 pu8Buf
    fclose(fpStrm); fpStrm = NULL; // JPG文件关闭
    
    // 检查JPG格式 计算jpg开始地址和数据长度
    HI_BOOL bFindStart   = HI_FALSE; // 是否找到图片的 首尾指针
    HI_U32 u32Start = 0; // 数据首指针位置  防止有多余数据,严格读取jpg格式
    HI_S32 ii;
    if (stVdecSend[0].enType == PT_MJPEG || stVdecSend[0].enType == PT_JPEG) // 这个模式
    { 
        // 找头指针
        for (ii=0; ii<s32ReadLen-1; ii++)
        {
            if (JPGfile_pu8Buf[ii] == 0xFF && JPGfile_pu8Buf[ii+1] == 0xD8) // 1111 1111 1101 1000
            {
                u32Start = ii; // 存储首指针位置
                // printf("\n%d\n", ii); // 0
                bFindStart = HI_TRUE;
                ii = ii + 2;
                break;
            }
        }

        // 统计
        for (; ii<s32ReadLen-3; ii++) // 整个编码数据是分段存储的,中间有标志位FFEx,随后跟着的是这段存储大小
        {
            if ((JPGfile_pu8Buf[ii] == 0xFF) && (JPGfile_pu8Buf[ii+1]& 0xF0) == 0xE0) // 1111 1111 1110 xxxx
            {
                // printf("%d\n", i); // 2 20 6532 14780 18503
                HI_U32 u32Len = (JPGfile_pu8Buf[ii+2]<<8) + JPGfile_pu8Buf[ii+3]; // FFEx后面接着的16位,对应长度,需要跳转这么多
                ii += 1 + u32Len;
            }
            else{
                break;
            }
        }

        for (; ii<s32ReadLen-1; ii++) // 找尾指针
        {
            if (JPGfile_pu8Buf[ii] == 0xFF && JPGfile_pu8Buf[ii+1] == 0xD9) // 1111 1111 1101 1001
            {
                // printf("%d\n", ii); // 394128
                break;
            }
        }
        s32ReadLen = ii+2; // 跳过尾字节

        if (bFindStart == HI_FALSE){ // 验证是否找到首指针
            printf("chn %d can not find JPEG start code!s32ReadLen %d.!\n", stVdecSend[0].s32ChnId, s32ReadLen);
            return HI_FAILURE;
        }
    }

    pstStream->u64PTS       = 0; // 码流包的时间戳us
    pstStream->pu8Addr      = JPGfile_pu8Buf + u32Start; // 有用数据首指针位置
    pstStream->u32Len       = s32ReadLen; // 数据总长度 (包括无用的数据)
    pstStream->bEndOfFrame  = HI_TRUE; // 当前帧是否结束
    pstStream->bEndOfStream = HI_FALSE; // 是否发送完所有码流 false表示要继续发送 true表示可以开始解码了
    pstStream->bDisplay     = 1;

    return s32Ret;
}
 


/* *****************************************************
* soft内存释放
******************************************************* */
void Efficient_Soft_Deinit(SVP_SOFT_EfficientDet *pstSoftParam){
    if(pstSoftParam->fp_result != NULL){ // 关闭file
        fclose(pstSoftParam->fp_result);
        pstSoftParam->fp_result = NULL;
    }
    if(0 != pstSoftParam->ps32ResultMem_beforeNMS.u64PhyAddr && 0 != pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr){
        SAMPLE_SVP_MMZ_FREE(pstSoftParam->ps32ResultMem_beforeNMS.u64PhyAddr, pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr);
        pstSoftParam->ps32ResultMem_beforeNMS.u64PhyAddr = 0;
        pstSoftParam->ps32ResultMem_beforeNMS.u64VirAddr = 0;
    }
    memset(&pstSoftParam, 0, sizeof(SVP_SOFT_EfficientDet));
}

